from flask import Blueprint, request, jsonify
import jwt
import hashlib
import hmac
import secrets
import time
from flask import current_app
from datetime import datetime, timedelta
from ..extensions import redis_conn, limiter
from ..config import Config
from ..utils import validate_device_id, get_device_secret
from ..models import Device

auth_bp = Blueprint('auth', __name__)

@auth_bp.route('/v1/ticket', methods=['POST'])
@limiter.limit("60/minute")
def create_ticket():
    # 请求验证
    if not request.is_json:
        return jsonify(Config.ERROR_CODES['INVALID_JSON']), 400
    
    data = request.get_json()
    required_fields = ['device_id', 'timestamp']
    if any(field not in data for field in required_fields):
        return jsonify(Config.ERROR_CODES['MISSING_FIELD']), 400
    
    device_id = data['device_id']
    timestamp = data['timestamp']
    
    # 设备ID验证
    if not validate_device_id(device_id):
        return jsonify(Config.ERROR_CODES['INVALID_DEVICE_ID']), 400
    
    # 设备存在性检查
    secret = get_device_secret(device_id)
    if not secret:
        return jsonify(Config.ERROR_CODES['UNKNOWN_DEVICE']), 401
    
    # 时间戳验证（±5分钟）
    try:
        # 将毫秒级时间戳转换为秒
        client_timestamp = int(timestamp) / 1000  
        current_timestamp = time.time()
        time_diff = abs(current_timestamp - client_timestamp)
           
        if time_diff > 300:
            logger.warning(f"过期时间戳: {timestamp} (转换后:{client_timestamp}), 当前时间戳: {current_timestamp}, 差: {time_diff}")
            return jsonify(
                code=Config.ERROR_CODES['EXPIRED_TIMESTAMP'][0],
                message=Config.ERROR_CODES['EXPIRED_TIMESTAMP'][1]
            ), 401
    except ValueError:
        logger.warning(f"非法时间戳格式: {timestamp}")
        return jsonify(
            code=40008,
            message="时间戳必须为13位毫秒级整数"
        ), 400
    
    # 生成票据
    ticket = secrets.token_urlsafe(64)
    redis_conn.redis.setex(
        f"ticket:{ticket}",
        Config.TICKET_EXPIRE_SECONDS,
        device_id
    )
    
    return jsonify({
        "code": 0,
        "ticket": ticket,
        "expiresIn": Config.TICKET_EXPIRE_SECONDS
    }), 201

@auth_bp.route('/v1/token', methods=['POST'])
def create_token():
    # Token生成逻辑
    if not request.is_json:
        return jsonify(Config.ERROR_CODES['INVALID_JSON']), 400
    
    data = request.get_json()
    required_fields = ['ticket', 'device_id', 'signature', 'timestamp']
    if any(field not in data for field in required_fields):
        return jsonify(Config.ERROR_CODES['MISSING_FIELD']), 400
    
    ticket = data['ticket']
    device_id = data['device_id']
    signature = data['signature']
    timestamp = data['timestamp']
    
    # 验证票据
    stored_device = redis_conn.redis.get(f"ticket:{ticket}")
    if not stored_device:
        return jsonify(Config.ERROR_CODES['INVALID_TICKET']), 401
    
    if stored_device != device_id:
        return jsonify(Config.ERROR_CODES['DEVICE_MISMATCH']), 401
    
    # 时间戳验证（±5分钟）
    try:
        # 将毫秒级时间戳转换为秒
        client_timestamp = int(timestamp) / 1000  
        current_timestamp = time.time()
        time_diff = abs(current_timestamp - client_timestamp)
           
        if time_diff > 300:
            logger.warning(f"过期时间戳: {timestamp} (转换后:{client_timestamp}), 当前时间戳: {current_timestamp}, 差: {time_diff}")
            return jsonify(
                code=Config.ERROR_CODES['EXPIRED_TIMESTAMP'][0],
                message=Config.ERROR_CODES['EXPIRED_TIMESTAMP'][1]
            ), 401
    except ValueError:
        logger.warning(f"非法时间戳格式: {timestamp}")
        return jsonify(
            code=40008,
            message="时间戳必须为13位毫秒级整数"
        ), 400
    
    # 使用ORM从数据库获取设备密钥
    device = Device.query.get(device_id)
    if not device:
        return jsonify(Config.ERROR_CODES['UNKNOWN_DEVICE']), 401
    
    secret = device.device_key  # 使用ORM获取设备密钥
    sign_str = f"{ticket}{device_id}{secret}"
    expected_sign = hashlib.md5(sign_str.encode()).hexdigest()
    
    if not hmac.compare_digest(signature.lower(), expected_sign.lower()):
        return jsonify(Config.ERROR_CODES['INVALID_SIGNATURE']), 401
    
    # 生成JWT
    token_payload = {
        "sub": device_id,
        "exp": datetime.utcnow() + timedelta(hours=Config.TOKEN_EXPIRE_HOURS),
        "iat": datetime.utcnow(),
        "jti": secrets.token_urlsafe(16)
    }
    access_token = jwt.encode(token_payload, Config.SECRET_KEY, algorithm="HS256")
    
    # 存储刷新令牌
    refresh_token = secrets.token_urlsafe(64)
    redis_conn.redis.setex(
        f"refresh_token:{refresh_token}",
        int(timedelta(days=30).total_seconds()),
        device_id
    )
    
    return jsonify({
        "code": 0,
        "token": access_token,
        "refreshToken": refresh_token,
        "expiresIn": Config.TOKEN_EXPIRE_HOURS * 3600
    }), 201
    
@auth_bp.route('/v1/token/refresh', methods=['POST'])
def refresh_token():
    """刷新访问令牌接口"""
    try:
        data = request.get_json()
        required_fields = ['refreshToken', 'device_id', 'signature', 'timestamp']
        if any(field not in data for field in required_fields):
            current_app.logger.error("缺少必要字段: %s", data)
            return jsonify({"code": 40006, "message": "Missing required fields"}), 400

        refresh_token = data['refreshToken']
        device_id = data['device_id']
        signature = data['signature']
        timestamp = data['timestamp']
        
        # 使用ORM从数据库获取设备密钥
        device = Device.query.get(device_id)
        if not device:
            current_app.logger.error("未知设备ID: %s", device_id)
            return jsonify({"code": 40101, "message": "Unknown device ID"}), 401
        
        secret = device.device_key  # 使用ORM获取设备密钥

        # 验证时间戳
        time_diff = abs(int(time.time()) - int(timestamp))
        if time_diff > 300:
            current_app.logger.warning("时间戳过期: %s", timestamp)
            return jsonify({"code": 40105, "message": "Expired timestamp"}), 401

        # 验证刷新令牌（修正redis访问方式）
        stored_device = redis_conn.redis.get(f"refresh_token:{refresh_token}")
        if not stored_device or stored_device != device_id:
            current_app.logger.warning("无效刷新令牌 | 设备:%s 令牌:%s...", device_id, refresh_token[:8])
            return jsonify({"code": 40108, "message": "Invalid refresh token"}), 401

        # 签名验证
        sign_str = f"{refresh_token}{device_id}{secret}"
        expected_sign = hashlib.md5(sign_str.encode()).hexdigest()
        
        if not hmac.compare_digest(expected_sign.lower(), signature.lower()):
            current_app.logger.warning("签名验证失败 | 设备:%s", device_id)
            return jsonify({"code": 40106, "message": "Invalid signature"}), 401
            
        # 生成新访问令牌（使用 current_app 配置）
        new_token_payload = {
            "sub": device_id,
            "exp": datetime.utcnow() + timedelta(hours=current_app.config['TOKEN_EXPIRE_HOURS']),
            "iat": datetime.utcnow(),
            "jti": secrets.token_urlsafe(16)
        }
        new_access_token = jwt.encode(
            new_token_payload,
            current_app.config['SECRET_KEY'],
            algorithm="HS256"
        )
        
        # 生成新刷新令牌
        new_refresh_token = secrets.token_urlsafe(64)
        
        # 原子化更新操作
        try:
            pipeline = redis_conn.redis.pipeline()
            pipeline.delete(f"refresh_token:{refresh_token}")
            pipeline.setex(
                f"refresh_token:{new_refresh_token}",
                int(timedelta(days=30).total_seconds()),
                device_id
            )
            pipeline.execute()
        except redis.exceptions.RedisError as e:
            current_app.logger.error("Redis操作失败: %s", str(e))
            return jsonify({"code": 50006, "message": "Token refresh failed"}), 500
            
        current_app.logger.info("令牌刷新成功 | 设备:%s", device_id)
        return jsonify({
            "code": 0,
            "token": new_access_token,
            "refreshToken": new_refresh_token,
            "expiresIn": current_app.config['TOKEN_EXPIRE_HOURS'] * 3600
        }), 200
        
    except Exception as e:
        current_app.logger.exception("令牌刷新异常")
        return jsonify({"code": 50007, "message": "Token refresh error"}), 500